/*CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
 C      process.c
 C      Application Name:   tcss422-project2
 C      Programmers Names:  Evan Phillips, Lucas Cowell, Tony Butterfield
 C      Date created:   May 11, 2011
 C      Date Completed: 
 C      Brief Description: 
 C
 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC */
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#include "definitions.h"
#include "process_queue.h"
#include "process.h"
#include "semaphore.h"

/*=================================================================================================
 =  Comment block for function: p_init()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      Process p_init(int, char*, int, int)
 =      Return value ranges: pointer to process
 =      Parameter value ranges: pid, name, number of steps, number of requests
 =      Error conditions: N/a
 =  Initializes a process.
 =
 ==================================================================================================*/
Process p_init(int pid, char* name, int num_steps, int num_requests)
{
    int error = 0;
    Process p = (Process) malloc(sizeof(processStr));
    p->pcb = pcb_init(pid, name);
    p->num_steps = num_steps;
    p->num_requests = num_requests;
    p->requests = req_init(num_requests, &error);

    //p_setState(p, P_READY);
    //p_setNextStep(p, 0);

    return p;
}

/*=================================================================================================
 =  Comment block for function: pcb_init()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      PCB pcb_init(int, char*)
 =      Return value ranges: pointer to process control block
 =      Parameter value ranges: int pid - process id#, char* name - name of process (20 char max)
 =      Error conditions: N/a
 =  Initializes a process control block.
 =
 ==================================================================================================*/
PCB pcb_init(int pid, char* name)
{
    PCB pcb = (PCB) malloc(sizeof(pcbStr));

    pcb->name = (char*) malloc(sizeof(char) * 20);  // name limited to 20 char's
    strcpy(pcb->name, name);                        // copy name to pcb

    pcb->next_step = 0;
    pcb->pid = pid;
    pcb->owns = NULL;
    pcb->state = P_READY;
    pcb->waiting_on = NULL;

    return pcb;
}

/*=================================================================================================
 =  Comment block for function: p_free()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_free(Process)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process
 =      Error conditions: NULL_POINTER_ERROR
 =  deallocates memory used by a Process, including the PCB and Request array.
 =
 ==================================================================================================*/
int p_free(Process p)
{
    if (p == NULL) return NULL_POINTER_ERROR;
      // TODO:  we really don't need to implement this, anyway.  the processes don't die until the
      //            main() terminates.
    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_setPID()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_setPID(Process, int)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process, int PID
 =      Error conditions: NULL_POINTER_ERROR,INVALID_ARGUMENT_ERROR
 =  sets the Process ID in the Process' Process Control Block
 =          (note: it's up to the Scheduler to make sure the ID is unique for each process)
 =
 ==================================================================================================*/
int p_setPID(Process p, int pid)
{
    if (p == NULL) return NULL_POINTER_ERROR;

    p->pcb->pid = pid;

    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_getPID()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getPID(Process, int*)
 =      Return value ranges: process ID as int, returns -1 on null pointer.
 =      Parameter value ranges: pointer to Process, int PID
 =      Error conditions: NULL_POINTER_ERROR
 =  gets the Process ID in the Process' Process Control Block
 =
 ==================================================================================================*/
int p_getPID(Process p, int* error)
{
    if (p == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return -1;
    }

    return p->pcb->pid;
}

/*=================================================================================================
 =  Comment block for function: p_setNextStep()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_setNextStep(Process, int)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process, number of steps in process as integer
 =      Error conditions: NULL_POINTER_ERROR, INVALID_ARGUMENT_ERROR
 =  sets the number of the interrupted step
 =
 ==================================================================================================*/
int p_setNextStep(Process p, int step_number)
{
    if (p == NULL) return NULL_POINTER_ERROR;

    p->pcb->next_step = step_number;// % p->num_steps;

    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_getNextStep()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getNextStep(Process, int*)
 =      Return value ranges: integer
 =      Parameter value ranges: pointer to Process, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR
 =  gets the number of the interrupted step
 =
 ==================================================================================================*/
int p_getNextStep(Process p, int* error)
{
    if (p == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return -1;
    }

    return p->pcb->next_step;
}

/*=================================================================================================
 =  Comment block for function: p_setState()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_setState(Process, int)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process, state of process
 =      Error conditions: NULL_POINTER_ERROR, INVALID_ARGUMENT_ERROR
 =  sets the current state of the process int the process control block.
 =
 ==================================================================================================*/
int p_setState(Process p, int state)
{
    if (p == NULL) return NULL_POINTER_ERROR;
    if (state < P_RUNNING || state > P_BLOCKED) return INVALID_ARGUMENT_ERROR;

    p->pcb->state = state;
    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_getState()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getState(Process, int*)
 =      Return value ranges: integer 0-3
 =      Parameter value ranges: pointer to Process, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR
 =  gets the current state of the process from its PCB
 =
 ==================================================================================================*/
int p_getState(Process p, int* error)
{
    if (p == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return -1;
    }

    return p->pcb->state;
}

/*=================================================================================================
 =  Comment block for function: p_setWaiting()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_setWaiting(Process, ProcessQueue)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process, pointer to blocking item - probably need to redo
 =              Blocking item can be NULL.
 =      Error conditions: NULL_POINTER_ERROR
 =  sets a pointer to the owner of the block queue the process is sitting in.
 =
 ==================================================================================================*/
int p_setWaiting(Process p, ProcessQueue q)
{
    if (p == NULL) return NULL_POINTER_ERROR;

    p->pcb->waiting_on = q;
    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_getWaiting()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getWaiting(Process, int*)
 =      Return value ranges: pointer to process_queue or NULL
 =      Parameter value ranges: pointer to Process, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR
 =  gets pointer to the item this process is currently waiting for
 =
 ==================================================================================================*/
ProcessQueue p_getWaiting(Process p, int* error)
{
    if (p == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return NULL;
    }

    return p->pcb->waiting_on;
}

/*=================================================================================================
 =  Comment block for function: p_setOwns()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_setOwns(Process, ProcessQueue)
 =      Return value ranges: 0 on success or error code
 =      Parameter value ranges: pointer to Process, pointer to blocking item - need to redo
 =                  blocking item can be set to NULL.
 =      Error conditions: NULL_POINTER_ERROR
 =  sets a pointer to the owner of the block queue the process is sitting in.
 =
 ==================================================================================================*/
int p_setOwns(Process p, Semaphore s)
{
    if (p == NULL) return NULL_POINTER_ERROR;

    p->pcb->owns = s;
    return 0;
}

/*=================================================================================================
 =  Comment block for function: p_getOwns()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getOwns(Process, int*)
 =      Return value ranges: pointer to process_queue or NULL
 =      Parameter value ranges: pointer to Process, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR
 =  gets pointer to the item this process is currently waiting for
 =
 ==================================================================================================*/
Semaphore p_getOwns(Process p, int* error)
{
    if (p == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return NULL;
    }

    return p->pcb->owns;
}

/*=================================================================================================
 =  Comment block for function: p_isRequest()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      BOOLEAN p_isRequest(Process, int, int*)
 =      Return value ranges: BOOLEAN
 =      Parameter value ranges: pointer to process, process step number, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR, INVALID_ARGUMENT_ERROR
 =  returns true if there is a request with a matching step number.
 =
 ==================================================================================================*/
BOOLEAN p_isRequest(Process p, int step, int* error)
{
    *error = 0;

    if (p == NULL || error == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return FALSE;
    }
    if (step < 0 || step >= p->num_steps)
    {
        *error |= INVALID_ARGUMENT_ERROR;
        return FALSE;
    };

    BOOLEAN is_request = FALSE;
    int i;
    for (i = 0; i < p->num_requests; i++)
    {
        if (p->requests[i]->step_number == step)
        {
            is_request = TRUE;
        }
    }

    return is_request;
}

/*=================================================================================================
 =  Comment block for function: p_getRequest()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      int p_getRequest(Process, int, int*)
 =      Return value ranges: int code for request type, else 0 if no requests
 =      Parameter value ranges: pointer to process, process step number, pointer to error code
 =      Error conditions: NULL_POINTER_ERROR, INVALID_ARGUMENT_ERROR
 =  returns the code for a interrupt request.
 =
 ==================================================================================================*/
int p_getRequest(Process p, int step, int* error)
{
    *error = 0;
    if (p == NULL || error == NULL)
    {
        *error |= NULL_POINTER_ERROR;
        return -1;
    }
    if (step < 0 || step >= p->num_steps)
    {
        *error |= INVALID_ARGUMENT_ERROR;
        return -1;
    }

    int request = 0;
    int i;
    for (i = 0; i < p->num_requests; i++)
    {
    	if(p->requests[i] != NULL) {
    		if (p->requests[i]->step_number == step) {
    			request = p->requests[i]->request_type;
    			break;
    		}
    	}
    }

    return request;
}

/*=================================================================================================
 =  Comment block for function: r_init()
 =  Programmer names: Evan Phillips, Lucas Cowell, Tony Butterfield
 =  Final test and approval date:
 =  Synopsis:
 =      Request* r_init(int)
 =      Return value ranges: pointer to an array of requests
 =      Parameter value ranges: size of array
 =      Error conditions: INVALID_ARGUMENT_ERROR
 =  allocates space for an array of requests
 =
 ==================================================================================================*/
Request* req_init(int num_interrupts, int* error)
{
    if (num_interrupts < 0)
    {
        *error |= INVALID_ARGUMENT_ERROR;
        return NULL;
    }

    if (num_interrupts == 0)
    {
        num_interrupts = 1;
    }

    Request* req_array = (Request*) malloc(sizeof(RequestTypeStr) * num_interrupts);

    int i;
    for (i = 0; i < num_interrupts; i++)
    {
        req_array[i] = (Request) malloc (sizeof(RequestTypeStr));
    }

    return req_array;
}
